package com.bajiao.app.aspect;

import com.bajiao.app.shiro.LoginUser;
import com.bajiao.common.aspect.annotation.LimitSubmit;
import com.bajiao.common.exception.DataHandleException;
import com.bajiao.common.util.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * 使用：
 *  @LimitSubmit(key = "testLimit:%s:#orderId",limit = 10,needAllWait = true)
 * 	%S 代表当前登录人
 * 	#参数 代表从参数中获取，支持多个参数
 * 生成的redis key： testLimit:e9ca23d68d884d4ebb19d07889727dae:order1123123
 *
 * 1.限制对某个接口的访问，针对所有人 ，则去除%S
 * 2.限制某个人对某个接口的访问，则 %S
 * 3.限制某个人对某个接口的业务参数的访问，则 %S:#参数1:#参数2
 * @Description:
 * @Author: FanJq
 * @Date: 2024-10-10
 * @Version: V1.0
 */
@Aspect
@Component
@Slf4j
class LimitSubmitAspect {

    //封装了redis操作各种方法
    @Autowired
    private RedisUtil redisUtil;
    @Pointcut("@annotation(com.bajiao.common.aspect.annotation.LimitSubmit)")
    private void pointcut() {}

    @Around("pointcut()")
    public Object handleSubmit(ProceedingJoinPoint joinPoint) throws Throwable {
        String userId = LoginUser.getCurrentUserId();
        if (StringUtils.isNotBlank(userId)) {
            Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
            //获取注解信息
            LimitSubmit limitSubmit = method.getAnnotation(LimitSubmit.class);
            int submitTimeLimiter = limitSubmit.limit();
            String redisKey = limitSubmit.key();
            boolean needAllWait = limitSubmit.needAllWait();
            String key =  getRedisKey(userId,joinPoint, redisKey);
            Object result = redisUtil.get(key);
            if (result != null) {
                throw new DataHandleException("请勿重复访问！",200);
            }
            redisUtil.set(  key,  userId,submitTimeLimiter);
            try {
                Object proceed = joinPoint.proceed();
                return proceed;
            } catch (Throwable e) {
                log.error("Exception in {}.{}() with cause = \'{}\' and exception = \'{}\'", joinPoint.getSignature().getDeclaringTypeName(),
                        joinPoint.getSignature().getName(), e.getCause() != null? e.getCause() : "NULL", e.getMessage(), e);
                throw e;
            }finally {
                if(!needAllWait) {
                    redisUtil.del(redisKey);
                }
            }
        }
        throw new DataHandleException("未登录状态的接口不记录！",200);
    }

    /**
     * 支持多参数，从请求参数进行处理
     */
    private String   getRedisKey(String userId, ProceedingJoinPoint joinPoint , String key ){
        if(key.contains("%s")) {
            key = String.format(key, userId);
        }
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();

        LocalVariableTableParameterNameDiscoverer discoverer = new LocalVariableTableParameterNameDiscoverer();
        String[] parameterNames = discoverer.getParameterNames(method);
        if (parameterNames != null) {
            for (int i=0; i < parameterNames.length; i++  ) {
                String item = parameterNames[i];
                if(key.contains("#"+item)){
                    key = key.replace("#"+item, joinPoint.getArgs()[i].toString());
                }
            }
        }
        return key.toString();
    }

}
